//模拟实现shell
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <ctype.h>
#include <fcntl.h>



#define LEFT "["
#define RIGHT "]"
#define LABLE "#"
#define DELIM " \t"
#define LINE_SIZE 1024
#define ARGC_SIZE 32
#define DELIM " \t"
#define NONE -1
#define IN_RDIR     0
#define OUT_RDIR    1
#define APPEND_RDIR 2
#define EXIT_CODE 44


// 自定义环境变量表 
int lastcode=0;
char myenv[LINE_SIZE];
int quit=0;
char commandline[LINE_SIZE];
char pwd[LINE_SIZE];
char* argv[ARGC_SIZE];
char *rdirfilename = NULL;
int rdir = NONE;
extern char **environ;

const char* _getusername()
{
  return getenv("USER");
}

const char* _gethostname()
{
  return getenv("HOSTNAME");
}

void getpwd()
{
   getcwd(pwd, sizeof(pwd));//用来获取当前工作目录的路径
}
void get_commandline(char*cline,int n)
{
    //打印命令行前面的提示信息
    getpwd();
    printf("[%s@%s %s]#",_getusername(), _gethostname(), pwd);
    char*s=fgets(cline,n,stdin);
    assert(s);

  //abcd\n\0;
   cline[strlen(cline)-1]='\0';//将\n的位置换成\0
}

int splitstring(char* cline,char * _argv[])
{
    int i=0;
    _argv[i++]=strtok(cline,DELIM);
    while(_argv[i++]=strtok(NULL,DELIM))
    {

    }
  return i-1;
}

int buildCommand(char* argv[],int n)
{
  if(n==2&&strcmp(argv[0],"cd")==0)
  {
      chdir(argv[1]);
      getpwd();
      sprintf(getenv("PWD"),"%s",pwd);// sprintf的作用是将一个格式化的字符串输出到一个目的字符串中，而printf是将一个格式化的字符串输出到屏幕。
      return 1;
  }
  else if(n==2&&strcmp(argv[0],"export")==0)
  {
      strcpy(myenv,argv[1]);
      putenv(myenv);//用于改变或增加环境变量的内容
      return 1;
  }
  else if(n==2&&strcmp(argv[0],"echo")==0)
  {
    if(strcmp(argv[1],"$?")==0)
    {
      printf("%d\n",lastcode);
      lastcode=0;
    }
   else if(*argv[1]=='$')
   {
      char*val=getenv(argv[1]+1);
      if(val)
      {
        printf("%s\n",val);
      }
   }
   else 
   {
     printf("%s\n",argv[1]);
   }
   return 1;
   
  }
  //特殊处理ls
  if(strcmp(argv[0], "ls") == 0)
    {
        argv[n++] = "--color";
        argv[n] = NULL;
    }
    return 0;
}
void NormalExcute(char* argv[])
{
    pid_t id=fork();
    if(id<0)
    {
      perror("fork");
      return ;
    }
    else if (id==0)//子进程
    {
      int fd=0;
      if(rdir==IN_RDIR)
      {
        fd=open(rdirfilename,O_RDONLY);
        dup2(fd,0);
      }
      else if(rdir == OUT_RDIR)
        {
            fd = open(rdirfilename, O_CREAT|O_WRONLY|O_TRUNC, 0666);
            dup2(fd, 1);
        }
        else if(rdir == APPEND_RDIR)
        {
            fd = open(rdirfilename, O_CREAT|O_WRONLY|O_APPEND, 0666);
            dup2(fd, 1);
        }
        //让子进程执行命令
        //execvpe(_argv[0], _argv, environ);
        execvp(argv[0], argv);
        exit(EXIT_CODE);

    }
    else//父进程 
    {
        int status=0;
        pid_t rid=waitpid(id,&status,0);//阻塞等待
        if(rid==id)
        {
           lastcode = WEXITSTATUS(status);
        }
    }
}
int main()
{
  while(!quit)
  {
    //获取命令行
    get_commandline(commandline,sizeof(commandline));
    //将命令解析成字符串
      int argc = splitstring(commandline, argv);
      if(argc==0)
      {
        continue;
      }
      //先执行看看是不是内建命令
      int n= buildCommand(argv, argc);
      //普通命令
      if(n==0)
      {
        NormalExcute(argv);
      }
  }


  return 0;
}
